/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.
 */

import { BasicFileSnapshot } from '../core/filesystem-snapshot.js';

import { FileSnapshotDetailDiff } from './filesnapshot-detail-diff.js';
import { FilesystemSnapshotDetailComparator } from './filesystem-snapshot-detail-comparator.js';
import { SnapshotDiffListener } from './snapshot-diff-listener.js';

/**
 * Snapshot DiffUtil
 *
 * @since 2022/9/1
 */
export class DiffUtil {
  static diff(oldSnapshot: BasicFileSnapshot, newSnapshot: BasicFileSnapshot, comparator: FilesystemSnapshotDetailComparator): boolean {
    const fileSnapShotDetailDiff = DiffUtil.diffWithDetail(oldSnapshot, newSnapshot, comparator);
    return (
      fileSnapShotDetailDiff.removedNodes.length === 0 && fileSnapShotDetailDiff.addedNodes.length === 0 && fileSnapShotDetailDiff.updatedNodes.length === 0
    );
  }

  /**
   * Snapshot diff
   *
   * @param oldSnapshot oldSnapshot
   * @param newSnapshot newSnapshot
   * @param comparator comparator
   */
  static diffWithDetail(
    oldSnapshot: BasicFileSnapshot,
    newSnapshot: BasicFileSnapshot,
    comparator: FilesystemSnapshotDetailComparator,
  ): FileSnapshotDetailDiff {
    const fileSnapshotDetailDiff = new FileSnapshotDetailDiff();

    const oldSnapshotIterator = oldSnapshot.walk();
    const newSnapshotIterator = newSnapshot.walk();

    let oldItem = oldSnapshotIterator.next();
    let newItem = newSnapshotIterator.next();

    while (!oldItem.done && !newItem.done) {
      // 不同的比较算法
      const compared = comparator.compare(oldItem.value, newItem.value);

      if (compared < 0) {
        fileSnapshotDetailDiff.removedNodes.push(oldItem.value);
        oldItem = oldSnapshotIterator.next();
      } else if (compared > 0) {
        fileSnapshotDetailDiff.addedNodes.push(newItem.value);
        newItem = newSnapshotIterator.next();
      } else {
        fileSnapshotDetailDiff.updatedNodes.push(newItem.value);
        oldItem = oldSnapshotIterator.next();
        newItem = newSnapshotIterator.next();
      }
    }

    while (!oldItem.done) {
      fileSnapshotDetailDiff.removedNodes.push(oldItem.value);
      oldItem = oldSnapshotIterator.next();
    }

    while (!newItem.done) {
      fileSnapshotDetailDiff.addedNodes.push(newItem.value);
      newItem = newSnapshotIterator.next();
    }

    return fileSnapshotDetailDiff;
  }

  /**
   * Diff with break 可以在任意结点提早结束比较 （参考 gradle）
   *
   * @param oldSnapshot oldSnapshot
   * @param newSnapshot newSnapshot
   * @param comparator comparator
   * @param diffListener diffListener
   */
  static diffWithBreak(
    oldSnapshot: BasicFileSnapshot,
    newSnapshot: BasicFileSnapshot,
    comparator: FilesystemSnapshotDetailComparator,
    diffListener: SnapshotDiffListener,
  ): boolean {
    const oldSnapshotIterator = oldSnapshot.walk();
    const newSnapshotIterator = newSnapshot.walk();

    let oldItem = oldSnapshotIterator.next();
    let newItem = newSnapshotIterator.next();

    while (!oldItem.done && !newItem.done) {
      const compared = comparator.compare(oldItem.value, newItem.value);

      if (compared < 0) {
        if (!diffListener.removed(oldItem.value)) {
          return false;
        }
        oldItem = oldSnapshotIterator.next();
      } else if (compared > 0) {
        if (!diffListener.added(newItem.value)) {
          return false;
        }
        newItem = newSnapshotIterator.next();
      } else {
        if (!diffListener.updated(oldItem.value, newItem.value)) {
          return false;
        }
        oldItem = oldSnapshotIterator.next();
        newItem = newSnapshotIterator.next();
      }
    }

    while (!oldItem.done) {
      if (!diffListener.removed(oldItem.value)) {
        return false;
      }
      oldItem = oldSnapshotIterator.next();
    }

    while (!newItem.done) {
      if (!diffListener.added(newItem.value)) {
        return false;
      }
      newItem = newSnapshotIterator.next();
    }

    return true;
  }
}
